package com.ciaojian.core.service;

import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ciaojian.core.constant.RedisConstant;
import com.ciaojian.core.enums.business.*;
import com.ciaojian.core.exception.EntityNotFoundException;
import com.ciaojian.core.mapper.OrderMapper;
import com.ciaojian.core.mapper.WorkerInviteMapper;
import com.ciaojian.core.mapper.WorkerMapper;
import com.ciaojian.core.model.Order;
import com.ciaojian.core.model.Worker;
import com.ciaojian.core.model.WorkerInvite;
import com.ciaojian.core.model.ao.DataDashboardAO;
import com.ciaojian.core.model.dto.WorkerDTO;
import com.ciaojian.core.model.vo.FeesetCostVo;
import com.ciaojian.core.model.vo.SalesFranchiseesVo;
import com.ciaojian.core.model.vo.SalesProfitVo;
import com.ciaojian.core.model.vo.WorkerCostVo;
import com.ciaojian.core.util.*;
import com.ciaojian.core.util.function.OrderFunctional;
import io.swagger.models.auth.In;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * <p>
 * 剪发订单 服务类
 * </p>
 *
 * @author ${author}
 * @since 2020-08-21
 */
public abstract class OrderService<M extends BaseMapper<T>, T> extends ServiceImpl<OrderMapper, Order> {
    @Resource
    private WorkerInviteService<WorkerInviteMapper, WorkerInvite> workerInviteService;
    @Resource
    private WorkerService<WorkerMapper, Worker> workerService;


    /**
     * 统计已完成订单
     *
     * @param orderType 订单类型
     * @param workerId  发型师ID
     * @param beginDate 开始时间
     * @param endDate   结束时间
     * @return 订单量
     */
    public Integer countCompleted(OrderOrderTypeEnum orderType,
                                  Integer workerId,
                                  Integer storeId,
                                  Date beginDate,
                                  Date endDate) {
        return this.lambdaQuery()
                .eq(Order::getOrderType, orderType)
                .in(Order::getStatus, 3, 5)
                .eq(workerId != null, Order::getWorkerId, workerId)
                .eq(storeId !=null,Order::getStoreId, storeId)
                .between(beginDate != null && endDate != null, Order::getAddTime, beginDate, endDate)
                .count();
    }

    /**
     * 统计次卡/免单支付的订单数
     *
     * @param params 参数
     * @return 次卡/免单支付的订单数
     */
    public Integer countFreeNum(DataDashboardAO params) {
        return lambdaQuery()
                .ge(params.getStartDate() != null, Order::getAddTime, params.getStartDate())
                .le(params.getEndDate() != null, Order::getAddTime, params.getEndDate())
                .eq(params.getStoreId() != null, Order::getStoreId, params.getStoreId())
                .eq(params.getCustomerType() != null, Order::getCustomerType, params.getCustomerType())
                .eq(params.getOrderType() != null, Order::getOrderType, params.getOrderType())
                .in(Order::getPayType, PayTypeEnum.FREE.getDescription(), PayTypeEnum.TIMES_CARD.getDescription())
                .count();
    }

    /**
     * 获取发型师服务中的订单数量
     *
     * @param workerId 发型师ID
     * @return 发型师服务中的订单数量
     */
    public Integer countServicingNum(Integer workerId) {
        return lambdaQuery().eq(Order::getWorkerId, workerId)
                .eq(Order::getStatus, 1).count();
    }

    /**
     * 获取发型师服务中的订单数量
     *
     * @param workerId 发型师ID
     * @return 发型师服务中的订单数量
     */
    //SELECT w.`id` FROM d_store s,
    //             d_worker w
    //                     LEFT JOIN d_scheduling ds ON (ds.worker_id = w.id AND ds.date = CURDATE() AND ds.status = 1)
    //        WHERE w.`store_id` = s.`id`
    //          AND w.`store_id` = 7
    //          AND w.status != 8
    //          AND is_remove = 0

    /**
     * 根据订单号获取订单
     *
     * @param orderNo 订单号
     * @return 订单
     */
    public Order getByOrderNo(String orderNo) {
        return Optional.ofNullable(this.lambdaQuery().eq(Order::getOrderNo, orderNo).one()).orElseThrow(
                () -> new EntityNotFoundException(Order.class, "orderNo", orderNo)
        );
    }

    /**
     * 统计用户已完成订单(只包含剪发订单)
     *
     * @param userId 用户
     * @return 用户剪发订单数
     */
    public Integer countCompletedHairCutByUser(Integer userId) {
        return this.lambdaQuery()
                .eq(Order::getUserId, userId)
                .in(Order::getOrderType, OrderOrderTypeEnum.NORMAL, OrderOrderTypeEnum.MEITUAN)
                .in(Order::getStatus, OrderStatusEnum.paid, OrderStatusEnum.complete).count();
    }

    /**
     * 统计用户已完成订单(包含所有订单类型)
     *
     * @param userId 用户
     * @return 已完成订单数量
     */
    public Integer countNumberOfCompleteByUserId(Integer userId) {
        return this.lambdaQuery()
                .eq(Order::getUserId, userId)
                .in(Order::getStatus, 3, 5)
                .count();
    }


    /**
     * 统计消费数量(包含所有订单类型)
     *
     * @param order 订单
     * @return 消费数量
     */
    public Integer countNumberOfComplete(Order order) {
        return this.lambdaQuery()
                .eq(Order::getUserId, order.getUserId())
                .in(Order::getStatus, 3, 5)
                .le(Order::getAddTime, order.getAddTime()).count();
    }

    /**
     * 计算排队等待人数和等待时间
     * <p>1.取号剪发,默认等待15分钟</p>
     * <p>2.取号剪发,获取微信授权为女性,默认等待20分钟</p>
     * <p>3.用户取号染发,默认等待20分钟</p>
     *
     * @param queueMapList 排队列表
     * @return 排队等待人数(waitNum)和等待时间(waitTime)
     */
    public Map<String, Integer> minuteTake(List<Map> queueMapList) {
        // 等待时间
        int minute = 0;
        for (Map map : queueMapList) {
            Integer type = MapUtil.getInt(map, "type");
            switch (type) {
                case 1:
                    minute += 20;
                    break;
                case 0:
                    String gender = MapUtil.getStr(map, "gender");
                    if (GenderEnum.female.getDescription().equals(gender)) {
                        minute += 20;
                    } else {
                        minute += 15;
                    }
                    break;
                default:
                    minute += 15;
                    break;
            }
        }
        Map<String, Integer> map = new HashMap<>(2);
        // 等待人数
        map.put("waitNum", queueMapList.size());
        // 等待时长
        map.put("waitTime", minute);
        return map;
    }

    /**
     * 计算排队等待人数和等待时间
     * <p>1.取号剪发,默认等待15分钟</p>
     * <p>2.取号剪发,获取微信授权为女性,默认等待20分钟</p>
     * <p>3.用户取号染发,默认等待20分钟</p>
     *
     * @param queueMapList 排队列表
     * @return 排队等待人数(waitNum)和等待时间(waitTime)
     */
    public Map<String, Integer> minuteTakes(List<Map> queueMapList) {

//        RedisUtil.getKey(RedisConstant.WORKER_START_SERVING_TIME + order.getId());
        // 等待时间
        int minute = 0;
        int count =0;
        for (Map map : queueMapList) {
            Integer type = MapUtil.getInt(map, "type");

            Integer id = MapUtil.getInt(map, "id");
            String key = RedisUtil.getKey("WORKER_START_SERVING_TIME:"+id);
            if (StringUtil.isBlank(key)){
                switch (type) {
                    case 1:
                        minute += 20;
                        break;
                    case 0:
                        String gender = MapUtil.getStr(map, "gender");
                        if (GenderEnum.female.getDescription().equals(gender)) {
                            minute += 20;
                        } else {
                            minute += 15;
                        }
                        break;
                    default:
                        minute += 15;
                        break;
                }
            }else {
                Date date = new Date();
                try {
                    date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(key);
                }catch (Exception e){

                }
                //服务预计结束时间
                date.setTime(date.getTime() + 15*60*1000);
                //当前时间
                Date date1 = new Date();
                long l = date.getTime() - date1.getTime();
                //时间差
                long l1 = l / 1000 / 60;
                if (l1 < 0){
                     l1 = 0L;
                }


                switch (type) {
                    case 1:
                        if (count==0){
                            minute += l1;
                            count++;
                        }else {
                            minute += 20;
                        }
                        break;
                    case 0:
                        String gender = MapUtil.getStr(map, "gender");
                        if (GenderEnum.female.getDescription().equals(gender)) {
                            if (count==0){
                                minute += l1;
                                count++;
                            }else {
                                minute += 20;
                            }
                        } else {
                            if (count==0){
                                minute += l1;
                                count++;
                            }else {
                                minute += 15;
                            }
                        }
                        break;
                    default:
                        if (count==0) {
                            minute += l1;
                            count++;
                        }else {
                            minute += 15;
                        }
                        break;
                }
            }
        }
        Map<String, Integer> map = new HashMap<>(2);
        // 等待人数
        map.put("waitNum", queueMapList.size());
        // 等待时长
        map.put("waitTime", minute);
        return map;
    }

    /**
     * 设置用户订单属性
     *
     * @param order 要设置的订单
     */
    public void setCustomerType(Order order) {
        // 顾客当前订单之前已完成和已支付的订单数
        Integer count = this.countNumberOfComplete(order);
        // 0次 = 未消费用户
        // 1次 = 首次消费用户
        // 2次-3次 = 生客
        // 4次-5次 = 熟客
        // 5次+ = 深度用户
        switch (count + 1) {
            case 1:
                order.setCustomerType(3);
                break;
            case 2:
            case 3:
                order.setCustomerType(0);
                break;
            case 4:
            case 5:
                order.setCustomerType(1);
                break;
            default:
                order.setCustomerType(2);
                break;
        }
    }

    /**
     * 设置邀请发型师
     *
     * @param order 订单
     */
    public void setWorker(Order order) {
        Integer inviteId = workerInviteService.getWorkerIdByUserId(order.getUserId());
        if (inviteId != null) {
            WorkerDTO workerDTO = workerService.getBaseMapper().selectWorkerDTO(inviteId);
            order.setStoreId(workerDTO.getStore().getId());
            order.setStoreName(workerDTO.getStore().getTitle());
            order.setInviterId(inviteId);
        }
    }

    /**
     * 完成订单
     *
     * @param order 订单
     * @return 完成返回 true
     */
    public boolean completeOrder(Order order) {
        if (order == null) {
            return false;
        }
        LogUtil.writeBusinessLog("完成订单,订单信息:{}", order.toString());
        if (!order.getStatus().equals(OrderStatusEnum.servicing) && !order.getStatus().equals(OrderStatusEnum.wait_pay)) {
            return false;
        }
        order.setStatus(OrderStatusEnum.paid);
        order.setPayTime(new Date());
        this.setCustomerType(order);
        return order.updateById();
    }

    /**
     * 完成订单
     *
     * @param orderId 订单id
     */
    public void completeOrderByMeituan(Integer orderId, PayTypeEnum payType) {
        Order order = new Order().selectById(orderId);
        Assert.notNull(order, "订单不存在");
        order.setPayType(payType.getDescription());
        completeOrder(order);
    }


    /**
     * 订单号生成方法
     * <p>当前时间 + 字符串 "I" + 订单id </p>
     *
     * @param orderId 订单id
     * @return 订单号
     */
    public String generateOrderNoByOrderId(Integer orderId) {
        String prefix = new SimpleDateFormat("yyyyMMddHHmmsss").format(new Date());
        return prefix.concat("I").concat(String.valueOf(orderId));
    }

    /**
     * 订单号生成方法
     * <p>当前时间 + 字符串 "U" + 订单id </p>
     *
     * @param userId 用户id
     * @return 订单号
     */
    public String generateOrderNoByUserId(Integer userId) {
        String prefix = new SimpleDateFormat("yyyyMMddHHmmsss").format(new Date());
//        int i = (int) (Math.random() * 900) + 100;
//        String s = Integer.toString(i);

        //获取自增 最后三位
        SnowFlake snowFlake = new SnowFlake(0, 0);
        long nextId = snowFlake.nextId();
        String str = Long.toString(nextId);
        String s = str.substring(str.length() - 3);

        return prefix.concat("U").concat(String.valueOf(userId)).concat(s);
    }

    /**
     * 根据订单类型生成订单号
     *
     * @return 订单号
     */
    public String genOrderNo(OrderNoTypeEnum orderNoType) {
        OrderFunctional orderFunctional = (type) -> {
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");

            //获取自增 最后三位
            SnowFlake snowFlake = new SnowFlake(0, 0);
            long nextId = snowFlake.nextId();
            String str = Long.toString(nextId);
            String s = str.substring(str.length() - 3);

            return type.getType() + sdf.format(date) + s;
        };
        return orderFunctional.generateOrderNo(orderNoType);
    }


    /**
     * 获取销售总额
     *
     * @param params 查询参数
     * @return 销售总额
     */
    public BigDecimal getSumPayTotal(DataDashboardAO params) {
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.select("ifnull(sum(pay_total),0) as sumPayTotal");
        wrapper.lambda()
                .ge(params.getStartDate() != null, Order::getAddTime, params.getStartDate())
                .le(params.getEndDate() != null, Order::getAddTime, params.getEndDate())
                .eq(params.getStoreId() != null, Order::getStoreId, params.getStoreId());

        if (params.getOrderType() != null && params.getOrderType().equals(OrderOrderTypeEnum.NORMAL)) {
            wrapper.lambda().in(Order::getOrderType, OrderOrderTypeEnum.NORMAL, OrderOrderTypeEnum.MEITUAN);
        }
        if (params.getOrderType() != null && params.getOrderType().equals(OrderOrderTypeEnum.MEMBER_DETAIL)) {
            wrapper.lambda().in(Order::getOrderType, OrderOrderTypeEnum.MEMBER_DETAIL);
        }

        wrapper.lambda().in(Order::getStatus, 3, 5);
        Map<String, Object> map = this.getMap(wrapper);
        return MapUtil.get(map, "sumPayTotal", BigDecimal.class);
    }

    /**
     * 按条件查询订单数
     *
     * @param params 查询条件
     * @return 订单数
     */
    public Integer countOrderNum(DataDashboardAO params) {
        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(params.getStartDate() != null, Order::getAddTime, params.getStartDate())
                .le(params.getEndDate() != null, Order::getAddTime, params.getEndDate())
                .eq(params.getStoreId() != null, Order::getStoreId, params.getStoreId())
                .eq(params.getCustomerType() != null, Order::getCustomerType, params.getCustomerType());
        if (params.getOrderType() != null && params.getOrderType().equals(OrderOrderTypeEnum.NORMAL)) {
            wrapper.in(Order::getOrderType, OrderOrderTypeEnum.NORMAL, OrderOrderTypeEnum.MEITUAN);
        }
        if (params.getOrderType() != null && params.getOrderType().equals(OrderOrderTypeEnum.MEMBER_DETAIL)) {
            wrapper.in(Order::getOrderType, OrderOrderTypeEnum.MEMBER_DETAIL);
        }
        wrapper.in(Order::getStatus, 3, 5);
        return this.count(wrapper);
    }

    public  Integer countCompletedStore(Integer storeId,OrderOrderTypeEnum orderType, Integer workerId, Date beginDate, Date endDate){
        return this.lambdaQuery()
                .eq(Order::getStoreId,storeId)
                .eq(Order::getOrderType, orderType)
                .in(Order::getStatus, 3, 5)
                .eq(workerId != null, Order::getWorkerId, workerId)
                .between(beginDate != null && endDate != null, Order::getAddTime, beginDate, endDate)
                .count();
    }


}